﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//int main()
//{
//	printf("%s\n", __FILE__);//进行编译的源文件
//	printf("%s\n", __DATE__);//文件被编译的日期
//	printf("%s\n", __TIME__);//文件被编译的时间
//	printf("%d\n", __LINE__);//文件当前的行号
//	//printf("%s\n", __STDC__);//err - 说明当前VS2022的环境是不完全支持ANSIC
//
//	return 0;
//}


//#define
//1.#define 定义符号（常量）
//2.#define 定义宏

//#define MAX 1000
//#define STR "hello world"
////#define forever for(;;)
//int main()
//{
//	int m = MAX;//MAX被替换为1000
//	printf("%d\n", MAX);
//	printf("%s\n", STR);
//	return 0;
//}


//for(;;)
//初始化部分，调整部分，判断部分，都可以省略，但是判断部分如果省略掉不写，
//就意味着条件恒为真，容易进入死循环。


//如果定义的stuff过长，可以分成几行写，除了最后一行外，每行的后面都加一个反斜杠（续行符）。
//#define DEBUG_PRINT printf("file:%s\tline:%d\t\
//							date:%s\ttime:%s\n",\
//							__FILE__,__LINE__,\
//							__DATE__,__TIME__)


//#define定义宏：
//#define机制包括了一个规定，允许把参数替换到文本中，这种实现通常称为宏（macro）或定义宏（define macro）.
//#define name(parament-list) stuff
 
//实现一个宏，计算一个数的平方
//#define SQUARE(x)((x)*(x))//宏在书写时，记得带上括号，预防计算顺序出错
//int main()
//{
//	int a = 5;
//	int ret = SQUARE(a+1);
//	//int ret = a * a;
//	printf("%d\n", ret);
//	return 0;
//}


//预防出现优先级问题
//#define DOUBLE(x) (x)+(x) //这种写法会到导致下方代码在执行时，出现优先级问题，*号优先级比+号高，
//先计算10*5+5 = 55
//#define DOUBLE(x) ((x)+(x))//整体带上括号不会出现优先级问题，下面运算顺序为10*(5+5) = 100;
//
//int main()
//{
//	int a = 5;
//	printf("%d\n", 10 * DOUBLE(a));
//
//	return 0;
//}


//带有副作用的红参数
//int main()
//{
//	int a = 1;
//	int b = 10;
//	//a = b + 1;//a = 11,b = 10
//	a = ++b;//a = 11,b = 11,前置、后置++\--存在副作用
//	return 0;
//}

//当宏参数在宏的定义中出现超过一次的时候，如果参数带有副作用，那么你在使用这个
//宏的时候就可能出现危险，导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。

//写一个宏，求2个数的较大值
//#define MAX(X,Y) ((X)>(Y) ? (X):(Y))
//
//int main()
//{
//	int a = 5;
//	int b = 7;
//	//int max = MAX(a, b);
//	//printf("max = %d\n", max);
//
//	int max = MAX(a++, b++);
//	printf("max = %d\n", max);//8
//	printf("a = %d\n", a);//6
//	printf("b = %d\n", b);//9
//
//	return 0;
//}


//宏替换的规则
//在程序中扩展#define定义符号和宏时，需要涉及一下几个方面
//1.在调用宏时，首先对参数进行检查，看看是否包含任何由#define定义的符号。如果时，它们首先
//被替换
//2.替换文随后被插入到程序中原本文件的位置。对于宏，参数名被他们的值所替换。
//3.最后，再次对结果文件进行描述，看看它是否包含任何由#define定义的符号，如果是，就重复上述处理过程。

//注意：
//1.宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏，不能出现递归。
//2.当预处理器搜索#define定义的符号的时候，字符串常量的内容并不被搜索。


//#define MAX(X,Y) ((X)>(Y) ? (X):(Y))
//#define M 10
//int main()
//{
//	int a = 3;
//	int b = 5;
//	//int m = MAX(a, M);
//	int m = MAX(a, MAX(M.3));
//
//	printf("m = %d\n", m);
//	printf("a = %d\n", a);
//	printf("b = %d\n", b);
//	return 0;
//}


//宏函数的对比
//宏通常被应用于执行简单的运算。
//比如在两个数中找出较大的一个值时，写成下面的宏，更有优势一些
//#define MAX(a,b) ((a)>(b)?(a):(b));

//为啥不用函数来完成这个任务？
//1.用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹
//2.更为重要的时函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用，
//反之宏可以适用于整型、长整型、浮点型等可以用‘>‘来比较的类型。宏的参数是类型无关的。

//int Max(int a, int b)
//{
//	int r = a > b ? a : b;
//	return r;
//
//}
//
//#define MAX(a,b) ((a)>(b) ? (a):(b))
//
//int main()
//{
//	int m1 = Max(3, -14);
//	int m2 = MAX(3, -14);
//	//int m2 = ((3)>(-14) ? (3):(-14)));
//	printf("m1 = %d\n", m1);
//	printf("m2 = %d\n", m2);
//	return 0;
//}


//和函数相比宏单独劣势：
// 1.每次使用宏的时候，一份宏定义的代码将插入到程序中，除非宏比较短，否则可能大幅度增加程序的长度。
// 2.宏是没法调试的
// 3.宏由于类型无关，也就不够严谨
// 4.宏可能会带来运算符优先级的问题，导致程序容易出错。

#define Malloc(n ,type) (type*)malloc(n*sizeof(type));
//使用
//int main()
//{
//	int* p = (int*)malloc(10 * sizeof(int));
//
//	int* ptr = Malloc(10,int);
//	//int  * ptr = (int*)malloc(10*sizeof(int));
//
//	return 0;
//}

//int main()
//{
//	//下面这两种写法是一样的
//	printf("hello" "world\n");
//	printf("helloworld\n");
//	return 0;
//}

//int main()
//{
//	int a = 1;
//	printf("the value of a is %d\n",a);
//
//	int b = 22;
//	printf("the value of b is %d\n", b);
//	
//	float f = 3.14f;
//	printf("the value of f is %f\n", f);
//
//	return 0;
//}


//#运算符
//#运算符将宏的一个参数转换为字符串字面量，它仅允许出现在带参数的宏的替换列表中。
//#运算符所执行的操作可以理解为”字符串化“。

//当我们有一个变量 int a = 10; 的时候，我们想打印出：the value of a is 10.
//可以这样写：
//#define PRINT(n) printf("the value of "#n " is %d",n)
//int main()
//{
//	int a = 10;
//	PRINT(a);
//	return 0;
//}

//#define Print(n,format) printf("the value of "#n " is " format"\n",n)
//
//int main()
//{
//	int a = 1;
//	Print(a, "%d");
//	printf("the value of ""a" "is " "%d" "\n",a);
//	
//	int b = 22;
//	Print(b, "%d");
//
//	float f = 3.14f;
//	Print(f, "%f");
//
//	return 0;
//}


//##运算符
//##可以吧位于它两边的符号合成一个符号，它允许宏定义从分离的文本片段创建标识符。##被称为记号粘合
//这样的连接必须产生一个合法的标识符，否则其结果就是未定义的。

//写一个函数求2个数的较大值的似乎，不哦天呐的数据类型就写不同的函数
//相当于一个函数模板
#define GENERIC_MAX(type) \
		type type##_max(type x,type y) \
		{  \
			return x > y ? x : y;  \
		} 

//定义函数
//GENERIC_MAX(int);//int_max
//GENERIC_MAX(float);//float_max
//
//int main()
//{
//	//调用预编译函数模板
//	int r1 = int_max(5, 6);
//	printf("r1 = %d\n", r1);
//
//	float r2 = float_max(3.1f,2.9f);
//	printf("r2 = %f\n", r2);
//	return 0;
//}

//命名约定
//一般来说函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。
//一般习惯：把宏名全部大写，函数名不要全部大写

//offsetof -- 宏 计算结构体成员相较于结构体起始位置的偏移量

//undef - 这条指令用来移除一个宏命令

//#define MAX 100
//int main()
//{
//	printf("%d\n", MAX);
//#undef MAX //移除了MAX的宏命令
//	printf("%d\n", MAX);
//	return 0;
//}

//命令行定义
//许多C的编译器提供了一种能力，允许在命令行中定义符号，用于启动编译过程。
//例如：当我们根据同一个源文件要编译出一个程序的不同版本的时候，这个特性有点用处。（假定某个程序中声明
// 了一个某个长度的数组，如果机器内存有限，我们需要一个很小的数组，但是另外一个机器内存大些，我们需要一个数组能够大些）


//比如：调试性的代码，删除可惜，保留又碍事，所以我们可以选择性的编译

//#define __DEBUG__ //如果不想要下面的代码编译，则注释这条宏指令
//int main()
//{
//	int i = 0;
//	int arr[10] = { 0 };
//	for (i = 0; i < 10; i++)
//	{
//		arr[i] = i;
//#ifdef __DEBUG__
//		printf("%d\n", arr[i]);
//#endif
//	}
//	return 0;
//}

//#define M 10
//int main()
//{
//#if M > 0
//	printf("hehe\n");
//#endif
//	return 0;
//}

//#define M 3
//int main()
//{
//#if M == 0
//	printf("hh\n");
//#elif M == 1
//	printf("hhh\n");
//#elif M == 2
//	printf("hhhh\n");
//#else
//	printf("ok\n");
//#endif
//	return 0;
//}


//条件编译
//判断是否被定义
//#define MAX 1
//int main()
//{
//#if defined(MAX)
//	printf("hhh\n");
//#endif

//如果没有定义，就是为假
//#ifdef MAX
//	printf("hehe\n");
//#endif
//	return 0;
//}

//#define MAX 1
//int main()
//{
////#if defined(MAX)
////	printf("hhh\n");
////#endif
//
//	//如果没有定义
//#ifndef MAX
//	printf("hehe\n");
//#endif
//	return 0;
//}










//--------------------------------------------------
//int main()
//{
//	printf("%s\n", __FILE__);
//	printf("%s\n", __DATE__);
//	printf("%s\n", __TIME__);
//	printf("%d\n", __LINE__);
//	//printf("%d\n", __STDC__);//err,说明当前VS2022的环境是不完全支持ANSIC
//	//如果编译器遵循ANSI C,其值为1，否则未定义
//	return 0;
//}



//#define 定义符号（常量）；
//#define 定义宏


//带有副作用的宏参数（有点危险）
//写一个宏，求2个数的较大值
//#define MAX(X,Y) ((X)>(Y)?(X):(Y))
//int main()
//{
//	int a = 3;
//	int b = 5;
//	int m = MAX(a++, b++);
//	//           4      6
//	//int m = ((a++) > (b++) ? (a++) : (b++));
//	//			3        5       \       7
//	printf("m = %d\n", m);
//	printf("a = %d\n", a);
//	printf("b = %d\n", b);
//	return 0;
//}


//宏替换的规则
//在程序中扩展#define定义符号


//int main()
//{
//	printf("hello ""world\n");
//	printf("helloworld\n");
//	return 0;
//}




//# 运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带参数的宏的替换列表中。
//# 运算符所执⾏的操作可以理解为”字符串化“。
//#define Print(n,format) printf("the value of "#n" is "format"\n",n)
//
////下面哪些代码中有相似之处，可以写成宏
//int main()
//{
//	int a = 1;
//	Print(a, "%d");
//	//printf("the value of a is %d\n",a);
//
//	int b = 20;
//	Print(b, "%db");
//
//	//printf("the value fof b is %d\n",b);
//
//	float f = 5.6f;
//	Print(f, "%f");
//
//	//printf("the value of f is %f\n",f);
//	return 0;
//}


//## 可以把位于它两边的符号合成一个符号，它允许宏定义从分离的文本片段创建标识符。
// ##